package com.stone.framework.user.service.impl;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.stone.framework.core.exception.BusinessException;
import com.stone.framework.core.util.Beans;
import com.stone.framework.core.util.Strings;
import com.stone.framework.message.user.MenuResBody;
import com.stone.framework.message.user.ResourcePageReqBody;
import com.stone.framework.message.user.ResourcePageResBody;
import com.stone.framework.message.user.ResourceReqBody;
import com.stone.framework.message.user.ResourceTreeResBody;
import com.stone.framework.user.entity.Resource;
import com.stone.framework.user.entity.RoleResource;
import com.stone.framework.user.mapper.PermissionMapper;
import com.stone.framework.user.mapper.ResourceMapper;
import com.stone.framework.user.service.ResourceService;

@Service
@Transactional(readOnly = true)
public class ResourceServiceImpl implements ResourceService {

    @Autowired
    private ResourceMapper resourceMapper;

    @Autowired
    private PermissionMapper permissionMapper;

    @Override
    public List<ResourceTreeResBody> buildTree() {
        List<Resource> list = this.queryAll();
        return list.stream().filter(resource -> resource.getId().equals(0L))
            .map(resource -> this.doBuildTree(resource, list)).collect(Collectors.toList());
    }

    @Override
    public List<MenuResBody> buildMenu(String username) {
        List<Map<String, Object>> list = permissionMapper.selectByUsername(username);
        return list.stream().filter(perm -> (perm.get("PARENT_ID")).equals(0)).map(perm -> this.doBuildMenu(perm, list))
            .collect(Collectors.toList());
    }

    @Override
    public IPage<ResourcePageResBody> queryPage(ResourcePageReqBody reqBody, Page<Resource> page) {
        Resource entity = new Resource();
        Beans.copyProperties(reqBody, entity);
        return resourceMapper.selectPage(page, this.condition(entity)).convert(e -> {
            ResourcePageResBody resBody = new ResourcePageResBody();
            Beans.copyProperties(e, resBody);
            return resBody;
        });
    }

    @Override
    @Transactional(readOnly = false)
    public void save(ResourceReqBody reqBody) {
        Resource entity = new Resource();
        Beans.copyProperties(reqBody, entity);
        Integer count = resourceMapper.selectCount(new QueryWrapper<Resource>().eq("code", entity.getCode()));
        if (count > 0) {
            throw new BusinessException("资源已存在，请重新输入");
        }
        resourceMapper.insert(entity);
    }

    @Override
    @Transactional(readOnly = false)
    public void modify(ResourceReqBody reqBody) {
        Resource entity = resourceMapper.selectById(reqBody.getId());
        if (entity == null) {
            throw new BusinessException("资源不存在");
        }
        Beans.copyProperties(reqBody, entity);
        Integer count = resourceMapper.selectCount(new QueryWrapper<Resource>().eq("code", entity.getCode()));
        if (count > 1) {
            throw new BusinessException("资源代码重复，请重新输入");
        }
        resourceMapper.updateById(entity);
    }

    @Override
    @Transactional(readOnly = false)
    public void delete(Long id) {
        Integer count = resourceMapper.selectCount(new QueryWrapper<Resource>().eq("parent_id", id));
        if (count > 0) {
            throw new BusinessException("请先删除子节点");
        }

        Integer relationCount = permissionMapper.selectCount(new QueryWrapper<RoleResource>().eq("resource_id", id));
        if (relationCount > 0) {
            throw new BusinessException("请先删除相关角色映射关系");
        }
        resourceMapper.deleteById(id);
    }

    private QueryWrapper<Resource> condition(Resource condition) {
        QueryWrapper<Resource> wrapper = new QueryWrapper<>();
        wrapper.like(Strings.notEmpty(condition.getCode()), "code", condition.getCode());
        wrapper.like(Strings.notEmpty(condition.getName()), "name", condition.getName());
        wrapper.like(Strings.notEmpty(condition.getDesc()), "desc_", condition.getDesc());
        wrapper.eq(Objects.nonNull(condition.getState()), "state", condition.getState());
        return wrapper;
    }

    private List<Resource> queryAll() {
        return resourceMapper.selectList(null);
    }

    private ResourceTreeResBody doBuildTree(Resource root, List<Resource> list) {
        List<ResourceTreeResBody> children =
            list.stream().filter(node -> root.getId().equals(node.getParentId())).map(node -> {
                ResourceTreeResBody child = this.doBuildTree(node, list);
                Beans.copyProperties(node, child);
                return child;
            }).sorted((o1, o2) -> o1.getOrder() - o2.getOrder()).collect(Collectors.toList());

        ResourceTreeResBody resBody = new ResourceTreeResBody();
        Beans.copyProperties(root, resBody);
        resBody.setChildren(children);
        return resBody;
    }

    private MenuResBody doBuildMenu(Map<String, Object> root, List<Map<String, Object>> list) {
        List<MenuResBody> children = list.stream().filter(node -> root.get("ID").equals(node.get("PARENT_ID")))
            .filter(node -> Resource.Type.MENU.name().equals(node.get("TYPE"))
                || Resource.Type.LIST.name().equals(node.get("TYPE")))
            .map(node -> {
                MenuResBody child = this.doBuildMenu(node, list);

                child.setId(((Integer)node.get("ID")).longValue());
                child.setParentId(((Integer)node.get("PARENT_ID")).longValue());
                child.setName((String)node.get("NAME"));
                child.setLabel((String)node.get("LABEL_"));
                child.setIcon((String)node.get("ICON"));
                child.setOrder((Integer)node.get("ORDER_"));
                return child;
            }).sorted((o1, o2) -> o1.getOrder() - o2.getOrder()).collect(Collectors.toList());

        MenuResBody resBody = new MenuResBody();

        resBody.setId(((Integer)root.get("ID")).longValue());
        resBody.setName((String)root.get("NAME"));
        resBody.setLabel((String)root.get("LABEL_"));
        resBody.setIcon((String)root.get("ICON"));
        resBody.setOrder((Integer)root.get("ORDER_"));
        resBody.setChildren(children);
        return resBody;
    }

}
